// Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{cmp::Ordering, fmt::Display, hash::Hash, ptr::NonNull, str::FromStr};

use nom::{character::complete::{alphanumeric1, char, u32}, combinator::{map, opt}, error::Error, Err, IResult};
use serde::{de::Visitor, Deserialize, Serialize};

#[derive(Debug)]
pub struct Version {
    string: String,
    pub(super) inner: Inner,
}

#[derive(Debug, Default)]
pub(super) struct Inner {
    pub(super) major: u32,
    pub(super) minor: Option<u32>,
    pub(super) patch: Option<u32>,
    pub(super) prerelease: Option<Identifier>,
    pub(super) build: Option<Identifier>,
}

#[derive(Debug, Clone, Copy)]
pub(super) struct Identifier(NonNull<str>);

#[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)]
pub enum VersionParseError {
    #[error("bad major number")]
    BadMajor,
    #[error("bad minor number")]
    BadMinor,
    #[error("bad patch number")]
    BadPatch,
    #[error("bad prerelease info")]
    BadPrerelease,
    #[error("bad build info")]
    BadBuild,
    #[error("bad suffix")]
    Incomplete,
}

impl Display for Version {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.string.fmt(f)
    }
}

impl Hash for Version {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.string.hash(state);
    }
}

impl PartialEq for Version {
    fn eq(&self, other: &Self) -> bool {
        self.inner == other.inner
    }
}

impl Eq for Version {}

impl PartialOrd for Version {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Version {
    fn cmp(&self, other: &Self) -> Ordering {
        self.inner.cmp(&other.inner)
    }
}

impl PartialEq for Inner {
    fn eq(&self, other: &Self) -> bool {
        self.major == other.major
        && (self.minor.is_none() || other.minor.is_none() || self.minor == other.minor)
        && (self.patch.is_none() || other.patch.is_none() || self.patch == other.patch)
        && self.prerelease == other.prerelease
        && self.build == other.build
    }
}

impl Eq for Inner {}

impl PartialOrd for Inner {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Inner {
    fn cmp(&self, other: &Self) -> Ordering {
        match self.major.cmp(&other.major) {
            Ordering::Equal => {},
            ord => return ord,
        }
        match self.minor.unwrap_or(0).cmp(&other.minor.unwrap_or(0)) {
            Ordering::Equal => {},
            ord => return ord,
        }
        match self.patch.unwrap_or(0).cmp(&other.patch.unwrap_or(0)) {
            Ordering::Equal => {},
            ord => return ord,
        }
        match (self.prerelease, other.prerelease) {
            (None, None) => {},
            (None, Some(_)) => return Ordering::Less,
            (Some(_), None) => return Ordering::Greater,
            (Some(id1), Some(id2)) => match id1.cmp(&id2) {
                Ordering::Equal => {},
                ord => return ord,
            },
        }
        match (self.build, other.build) {
            (None, None) => Ordering::Equal,
            (None, Some(_)) => Ordering::Less,
            (Some(_), None) => Ordering::Greater,
            (Some(id1), Some(id2)) => id1.cmp(&id2),
        }
    }
}

impl PartialEq for Identifier {
    fn eq(&self, other: &Self) -> bool {
        unsafe { self.0.as_ref() == other.0.as_ref() }
    }
}

impl Eq for Identifier {}

impl PartialOrd for Identifier {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Identifier {
    fn cmp(&self, other: &Self) -> Ordering {
        unsafe { self.0.as_ref().cmp(other.0.as_ref()) }
    }
}

impl Clone for Version {
    fn clone(&self) -> Self {
        let string = self.string.clone();
        let inner = self.inner.clone(&self.string, &string);
        Self {
            string,
            inner,
        }
    }
}

unsafe impl Send for Version {}

unsafe impl Sync for Version {}

impl Version {
    pub fn from_major(major: u32) -> Self {
        let inner = Inner::from_major(major);
        Self {
            string: major.to_string(),
            inner,
        }
    }

    pub fn from_major_minor(major: u32, minor: u32) -> Self {
        let inner = Inner::from_major_minor(major, minor);
        Self {
            string: format!("{}.{}", major, minor),
            inner,
        }
    }

    pub fn from_standard(major: u32, minor: u32, patch: u32) -> Self {
        let inner = Inner::from_standard(major, minor, patch);
        Self {
            string: format!("{}.{}.{}", major, minor, patch),
            inner,
        }
    }

    pub fn as_str(&self) -> &str {
        self.string.as_str()
    }
}

impl Inner {
    fn from_major(major: u32) -> Self {
        Self {
            major,
            ..Default::default()
        }
    }

    fn from_major_minor(major: u32, minor: u32) -> Self {
        Self {
            major,
            minor: Some(minor),
            ..Default::default()
        }
    }

    fn from_standard(major: u32, minor: u32, patch: u32) -> Self {
        Self {
            major,
            minor: Some(minor),
            patch: Some(patch),
            ..Default::default()
        }
    }

    fn clone(&self, origin_str: &str, new_str: &str) -> Self {
        let prerelease = self.prerelease
            .as_ref()
            .map(|id| id.clone(origin_str, new_str));
        let build = self.build
            .as_ref()
            .map(|id| id.clone(origin_str, new_str));
        Self {
            major: self.major,
            minor: self.minor,
            patch: self.patch,
            prerelease,
            build,
        }
    }

    fn parse_dot_u32(i: &str) -> IResult<&str, u32> {
        let (i, _) = char('.')(i)?;
        u32(i)
    }

    fn parse_prerelease(i: &str) -> IResult<&str, Identifier> {
        let (i, _) = char('-')(i)?;
        map(alphanumeric1, |s: &str| Identifier::from(s))(i)
    }

    fn parse_build(i: &str) -> IResult<&str, Identifier> {
        let (i, _) = char('+')(i)?;
        map(alphanumeric1, |s: &str| Identifier::from(s))(i)
    }
}

impl Identifier {
    fn clone(&self, origin_str: &str, new_str: &str) -> Self {
        let str = unsafe { self.0.as_ref() };
        let offset = str.as_ptr() as usize - origin_str.as_ptr() as usize;
        let new_str = &new_str[offset..offset+str.len()];
        Self::from(new_str)
    }
}

impl TryFrom<String> for Version {
    type Error = VersionParseError;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        let inner = Inner::from_str(value.as_str())?;
        Ok(Self {
            string: value,
            inner,
        })
    }
}

impl FromStr for Version {
    type Err = VersionParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let inner = Inner::from_str(s)?;
        Ok(Self {
            string: s.to_string(),
            inner,
        })
    }
}

impl Serialize for Version {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer
    {
        serializer.serialize_str(self.as_str())
    }
}

impl<'de> Deserialize<'de> for Version {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>
    {
        struct VersionVisitor;

        impl Visitor<'_> for VersionVisitor {
            type Value = Version;
        
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("version")
            }

            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
                where
                    E: serde::de::Error
            {
                Version::from_str(v)
                    .map_err(serde::de::Error::custom)
            }
        }
        
        deserializer.deserialize_str(VersionVisitor)
    }
}

impl FromStr for Inner {
    type Err = VersionParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let (i, major) = u32(s)
            .map_err(|_: Err<Error<_>>| VersionParseError::BadMajor)?;
        let (i, minor) = opt(Self::parse_dot_u32)(i)
            .map_err(|_| VersionParseError::BadMinor)?;
        let (i, patch) = opt(Self::parse_dot_u32)(i)
            .map_err(|_| VersionParseError::BadPatch)?;
        let (i, prerelease) = opt(Self::parse_prerelease)(i)
            .map_err(|_| VersionParseError::BadPrerelease)?;
        let (i, build) = opt(Self::parse_build)(i)
            .map_err(|_| VersionParseError::BadBuild)?;

        if !i.is_empty() {
            return Err(VersionParseError::Incomplete);
        }

        let version = Inner {
            major,
            minor,
            patch,
            prerelease,
            build,
        };

        Ok(version)
    }
}

impl From<&str> for Identifier {
    fn from(value: &str) -> Self {
        Self(NonNull::from(value))
    }
}

#[cfg(test)]
mod test {
    use std::str::FromStr;

    use crate::versions::version::{Identifier, Inner, VersionParseError};

    use super::Version;

    #[test]
    fn test_version_eq() {
        assert_eq!(Version::from_major(1), Version::from_major(1));
        assert_eq!(Version::from_major(1), Version::from_major_minor(1, 2));
        assert_eq!(Version::from_major(1), Version::from_standard(1, 2, 3));
        assert_eq!(Version::from_major_minor(1, 2), Version::from_standard(1, 2, 3));

        assert_ne!(Version::from_major(1), Version::from_str("1.2.3-alpha").unwrap());
        assert_ne!(Version::from_major(1), Version::from_str("1+a1").unwrap());
    }

    #[test]
    fn test_version_ord() {
        assert!(Version::from_major(1) < Version::from_major(2));
        assert!(Version::from_major(1) < Version::from_major_minor(1, 1));
        assert!(Version::from_major(1) < Version::from_standard(1, 0, 1));
        assert!(Version::from_major(1) <= Version::from_standard(1, 0, 0));
        assert!(Version::from_major_minor(1, 0) < Version::from_major_minor(1, 1));
        assert!(Version::from_standard(1, 0, 0) < Version::from_standard(1, 0, 1));

        assert!(Version::from_major(1) < Version::from_str("1.2.3-alpha").unwrap());
        assert!(Version::from_major(1) < Version::from_str("1+a1").unwrap());
        assert!(Version::from_str("1.2.3-alpha").unwrap() < Version::from_str("1.2.3-beta").unwrap());
    }

    #[test]
    fn test_parse_prerelease() {
        assert_eq!(Inner::parse_prerelease("-alpha"), Ok(("", Identifier::from("alpha"))));
        assert_eq!(Inner::parse_prerelease("-rc1"), Ok(("", Identifier::from("rc1"))));
    }

    #[test]
    fn test_parse_build() {
        assert_eq!(Inner::parse_build("+abc123"), Ok(("", Identifier::from("abc123"))));
    }

    #[test]
    fn test_inner_from_str() {
        assert_eq!(
            Inner::from_str("").unwrap_err(),
            VersionParseError::BadMajor
        );
        assert_eq!(
            Inner::from_str("1"),
            Ok(Inner::from_major(1))
        );
        assert_eq!(
            Inner::from_str("1.2"),
            Ok(Inner::from_major_minor(1, 2))
        );
        assert_eq!(
            Inner::from_str("1.2.3"),
            Ok(Inner::from_standard(1, 2, 3))
        );
        assert_eq!(
            Inner::from_str("01.002.03"),
            Ok(Inner::from_standard(1, 2, 3))
        );
        assert_eq!(
            Inner::from_str("1.2-alpha"),
            Ok(Inner {
                major: 1,
                minor: Some(2),
                patch: None,
                prerelease: Some(Identifier::from("alpha")),
                build: None,
            })
        );
        assert_eq!(
            Inner::from_str("1.2.3-alpha"),
            Ok(Inner {
                major: 1,
                minor: Some(2),
                patch: Some(3),
                prerelease: Some(Identifier::from("alpha")),
                build: None,
            })
        );
        assert_eq!(
            Inner::from_str("1.2.3-alpha+a1b2"),
            Ok(Inner {
                major: 1,
                minor: Some(2),
                patch: Some(3),
                prerelease: Some(Identifier::from("alpha")),
                build: Some(Identifier::from("a1b2")),
            })
        );
        assert_eq!(
            Inner::from_str("1.2.3-alpha+a1b2#error").unwrap_err(),
            VersionParseError::Incomplete
        );
    }

    #[test]
    fn test_version_clone() {
        let version = Version::try_from(String::from("1.2.3-alpha+a1b2")).unwrap();
        let version_clone = version.clone();
        assert_eq!(version, version_clone);
        assert_ne!(version.inner.prerelease.unwrap().0, version_clone.inner.prerelease.unwrap().0);
        assert_ne!(version.inner.build.unwrap().0, version_clone.inner.build.unwrap().0);
    }

    #[test]
    fn test_version_serialize() {
        let mut s = String::new();
        serde::Serialize::serialize(
            &Version::from_standard(1, 2, 3),
            toml::ser::ValueSerializer::new(&mut s)
        ).unwrap();

        assert_eq!(
            &s,
            "\"1.2.3\""
        );
    }

    #[test]
    fn test_version_deserialize() {
        assert_eq!(
            serde::Deserialize::deserialize(toml::de::ValueDeserializer::new("\"1.2.3\"")),
            Ok(Version::from_standard(1, 2, 3))
        );
    }
}